package cn.ibizlab.util.service;

import cn.ibizlab.util.security.AuthenticationInfo;
import cn.ibizlab.util.security.AuthenticationUser;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import lombok.SneakyThrows;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.ObjectUtils;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

public interface UserServiceAdapter {

    DateFormat dtFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    default AuthenticationUser loadUserByUsername(String username){
        return null ;
    }

    default AuthenticationInfo loadUserByLogin(String username, String password){
        return null ;
    }

    default Map<String,Object> getAppData(String systemId,boolean isEnablePermissionValid)
    {

        Map<String,Object> appData = new HashMap<>() ;
        Set<String> appMenu = new HashSet<>();
        Set<String> uniRes = new HashSet<>();

        AuthenticationUser curUser = AuthenticationUser.getAuthenticationUser();
        if(isEnablePermissionValid&&(!ObjectUtils.isEmpty(systemId))){
            Collection<GrantedAuthority> authorities=curUser.getAuthorities();
            Iterator<GrantedAuthority> it = authorities.iterator();
            while(it.hasNext()) {
                GrantedAuthority authority = it.next();
                String strAuthority=authority.getAuthority();
                if(strAuthority.startsWith("UNIRES_"+systemId)) {
                    uniRes.add(strAuthority.substring(systemId.length()+8));
                }
                else if(strAuthority.startsWith("APPMENU_"+systemId)){
                    appMenu.add(strAuthority.substring(systemId.length()+9));
                }
            }
        }
        if (!ObjectUtils.isEmpty(curUser.getExpiration())) {
            appData.put("expireddate", dtFormat.format(curUser.getExpiration()));
        }
        Map<String,Object> context = new HashMap<>();
        context.putAll(curUser.getSessionParams());
        context.put("srfusername",curUser.getDisplayName());
        appData.put("context",context);
        appData.put("unires",uniRes);
        appData.put("appmenu",appMenu);
        appData.put("enablepermissionvalid",isEnablePermissionValid);
        if(curUser.isSuperUser()){
            appData.put("enablepermissionvalid",false);
        }
        else{
            appData.put("enablepermissionvalid",isEnablePermissionValid);
        }
        return appData;
    }

    default Map<String,List<String>> getOrgInfo(String organizationId, String departmentId) {
        return new HashMap<>();
    }

    default String getSecret() {
        return "ibzsecret";
    }

    default Long getExpiration() {
        return 7200000L;
    }

    @SneakyThrows
    default String generateToken(UserDetails userDetails) {
        Date now = new Date();
        JWSSigner signer = new MACSigner(getSecret());
        JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.HS512)
                .contentType("JWS")
                .build();
        JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
                .subject(userDetails.getUsername())
                .issueTime(now)
                .expirationTime(new Date(now.getTime()+getExpiration()))
                .build();
        SignedJWT signedJWT = new SignedJWT(header,claimsSet);
        signedJWT.sign(signer);
        return signedJWT.serialize();
    }

    @SneakyThrows
    default String getUsernameFromToken(String token) {
        SignedJWT signedJWT = SignedJWT.parse(token);
        JWTClaimsSet claimsSet = signedJWT.getJWTClaimsSet();
        return claimsSet.getSubject();
    }

    @SneakyThrows
    default Date getExpirationDateFromToken(String token) {
        SignedJWT signedJWT = SignedJWT.parse(token);
        JWTClaimsSet claimsSet = signedJWT.getJWTClaimsSet();
        return claimsSet.getExpirationTime();
    }

    @SneakyThrows
    default Boolean validateToken(String token, UserDetails userDetails) {
        Date now = new Date();
        JWSVerifier verifier = new MACVerifier(getSecret());
        SignedJWT signedJWT = SignedJWT.parse(token);
        JWTClaimsSet claimsSet = signedJWT.getJWTClaimsSet();
        if(signedJWT.verify(verifier) && now.before(claimsSet.getExpirationTime())){
            if(userDetails instanceof AuthenticationUser){
                AuthenticationUser user = (AuthenticationUser) userDetails;
                user.setToken(token);
                user.setExpiration(claimsSet.getExpirationTime());
            }
            return true;
        }
        return false;
    }



}
